#include <stdio.h>
#include <string.h>

#include <dicom/dicom.h>
#include <dicop/dicop.h>
#include "ipms_client_api.h"

#define TESTAPI

struct dicop_app *AppIPMS; /* TODO: To support multi APPs in one client */
LIST_HEAD(PCDListHead);

static void __StateChangeSignal(callback_args_t args);
static void __AutoReportSignal(callback_args_t args);
static void (*__SignalChangeCallback)(int ID, char *Datetime, int Event);
static void (*__AutoReportCallback)(int ID, PCDReportData_t *Report);


int IPMS_InitAPI(void)
{
    dicop_init();
    return 0;
}

int IPMS_Release(void)
{
    dicop_release();
    return 0;
}

int IPMS_Connect(char *IP, int Port)
{
    int ConnID;

    if (IP == NULL)
        return API_ERR_INVAL_ARG;

    ConnID = dicop_client_connect(IP, Port);
    if (ConnID < 0)
        return API_ERR_BAD_SOCKET;

    AppIPMS = dicop_app_query(ConnID, "IPMS");
    if (AppIPMS) {
        dicop_signal_connect(AppIPMS, "StateChange", __StateChangeSignal);
        dicop_signal_connect(AppIPMS, "AutoReport", __AutoReportSignal);
    }
    else {
        printf("IPMS Server is not ready\n");
        return API_ERR_BAD_CONN;
    }

    __SignalChangeCallback = NULL;
    __AutoReportCallback = NULL;

    return ConnID;
}

int IPMS_Disconnect(int ConnID)
{
    if (ConnID == 0)
        return API_ERR_BAD_CONN;

    if (AppIPMS)
        dicop_app_destroy(AppIPMS);

    dicop_client_disconnect(ConnID);
    return 0;
}

PCDInfo_t *IPMS_PCDRegister(char *MAC, char *Desc)
{
    PCDInfo_t *PCDInfo;
    int Session;
    int ID;
    char StrID[8] = {0};

    Session = dicop_method_call(AppIPMS, "QueryID", "MAC", MAC);
    dicop_method_call_get_reply(AppIPMS, "QueryID", Session, "ID", StrID);

    ID = atoi(StrID);
    if (ID < 0)
        return NULL;

    PCDInfo = malloc(sizeof(PCDInfo_t));
    if (PCDInfo == NULL)
        return NULL;

    INIT_LIST_HEAD(&PCDInfo->node);
    PCDInfo->ID = ID;
    strncpy(PCDInfo->MAC, MAC, 24);
    strncpy(PCDInfo->Description, Desc, 128);

    list_add_tail(&PCDListHead, &PCDInfo->node);

    return PCDInfo;
}

int IPMS_PCDUnregister(int ID)
{

}

int IPMS_PCDGetList(PCDInfo_t *List[], int *Count)
{

}

char *IPMS_PCDGetMAC(int ID)
{
    static char MAC[24] = {0};
    int Session;

    Session = dicop_method_call(AppIPMS, "RequestMAC", "ID", itoa(ID));
    dicop_method_call_get_reply(AppIPMS, "RequestMAC", Session, "MAC", MAC);

    return MAC;
}

int IPMS_PCDGetID(char *MAC)
{
    char StrID[8] = {0};
    int Session;

    Session = dicop_method_call(AppIPMS, "QueryID", "MAC", MAC);
    dicop_method_call_get_reply(AppIPMS, "QueryID", Session, "ID", StrID);

    return atoi(StrID);
}

PCDReportData_t *IPMS_PCDGetReport(int ID)
{
    PCDReportData_t *Report;
    int Session;
    char StrID[8] = {0};
    char StrVolt[8] = {0};
    char StrCurr[8] = {0};
    char StrInstPower[8] = {0};
    char StrAccumPower[16] = {0};

    Report = malloc(sizeof(PCDReportData_t));
    if (Report == NULL)
        return NULL;

    dicop_method_call_no_reply(AppIPMS, "SelectReportType", "ID", itoa(ID), "Selection", itoa(API_REPORT_SEL_ALL));
    Session = dicop_method_call(AppIPMS, "RequestReport", "ID", itoa(ID));
    dicop_method_call_get_reply(AppIPMS, "RequestReport", Session,
        "ID", StrID, "Datetime", Report->Datetime,
        "Voltage", StrVolt, "Current", StrCurr,
        "InstantPower", StrInstPower, "AccumPower", StrAccumPower);

    Report->Voltage = atoi(StrVolt);
    Report->Current = atoi(StrCurr);
    Report->InstantPower = atoi(StrInstPower);
    Report->AccumPower = atoi(StrAccumPower);

    return Report;
}

int IPMS_PCDGetVoltage(int ID)
{
    char StrVolt[8] = {0}, Dummy[20] = {0};
    int Session;

    dicop_method_call_no_reply(AppIPMS, "SelectReportType", "ID", itoa(ID), "Selection", itoa(API_REPORT_SEL_VOLT));
    Session = dicop_method_call(AppIPMS, "RequestReport", "ID", itoa(ID));
    dicop_method_call_get_reply(AppIPMS, "RequestReport", Session,
        "ID", Dummy, "Datetime", Dummy, "Voltage", StrVolt, "Current", Dummy,
        "InstantPower", Dummy, "AccumPower", Dummy);

    return atoi(StrVolt);
}

int IPMS_PCDGetCurrent(int ID)
{
    char StrCurr[8] = {0}, Dummy[20] = {0};
    int Session;

    dicop_method_call_no_reply(AppIPMS, "SelectReportType", "ID", itoa(ID), "Selection", itoa(API_REPORT_SEL_CURR));
    Session = dicop_method_call(AppIPMS, "RequestReport", "ID", itoa(ID));
    dicop_method_call_get_reply(AppIPMS, "RequestReport", Session,
        "ID", Dummy, "Datetime", Dummy, "Voltage", Dummy, "Current", StrCurr,
        "InstantPower", Dummy, "AccumPower", Dummy);

    return atoi(StrCurr);
}

int IPMS_PCDGetInstantPower(int ID)
{
    char StrInstPower[8] = {0}, Dummy[20] = {0};
    int Session;

    dicop_method_call_no_reply(AppIPMS, "SelectReportType", "ID", itoa(ID), "Selection", itoa(API_REPORT_SEL_INST_ENERG));
    Session = dicop_method_call(AppIPMS, "RequestReport", "ID", itoa(ID));
    dicop_method_call_get_reply(AppIPMS, "RequestReport", Session,
        "ID", Dummy, "Datetime", Dummy, "Voltage", Dummy, "Current", Dummy,
        "InstantPower", StrInstPower, "AccumPower", Dummy);

    return atoi(StrInstPower);
}

int IPMS_PCDGetAccumPower(int ID)
{
    char StrAccumPower[16] = {0}, Dummy[20] = {0};
    int Session;

    dicop_method_call_no_reply(AppIPMS, "SelectReportType", "ID", itoa(ID), "Selection", itoa(API_REPORT_SEL_ACCUM_ENERG));
    Session = dicop_method_call(AppIPMS, "RequestReport", "ID", itoa(ID));
    dicop_method_call_get_reply(AppIPMS, "RequestReport", Session,
        "ID", Dummy, "Datetime", Dummy, "Voltage", Dummy, "Current", Dummy,
        "InstantPower", Dummy, "AccumPower", StrAccumPower);

    return atoi(StrAccumPower);
}

int IPMS_AutoReportStart(int ID, unsigned int Time, int Selection)
{
    dicop_method_call_no_reply(AppIPMS, "SelectReportType", "ID", itoa(ID), "Selection", itoa(Selection));
    dicop_method_call_no_reply(AppIPMS, "SetAutoReportTimer", "ID", itoa(ID), "Time", itoa(Time * 10));
    return 0;
}

int IPMS_AutoReportStop(int ID)
{
    dicop_method_call_no_reply(AppIPMS, "SetAutoReportTimer", "ID", itoa(ID), "Time", "0");
    return 0;
}

int IPMS_PCDControl(int ID, unsigned int Type, unsigned int Ctrl, unsigned short Time)
{
    switch (Type) {
        case API_CTRL_TYPE_ONOFF:
            if (Ctrl == 1)
                dicop_method_call_no_reply(AppIPMS, "SetTurnOnTimer", "ID", itoa(ID), "Time", itoa(Time * 10));
            else
                dicop_method_call_no_reply(AppIPMS, "SetTurnOffTimer", "ID", itoa(ID), "Time", itoa(Time * 10));

            break;
    }

    return 0;
}

int IPMS_InstallEventHandler(int EventType, void *handler)
{
    switch (EventType) {
        case API_EVENT_STAT_CHANGE:
            __SignalChangeCallback = handler;
            break;
        case API_EVENT_AUTO_REPORT:
            __AutoReportCallback = handler;
            break;
        default:
            return -1;
    }

    return 0;
}

static void __StateChangeSignal(callback_args_t args)
{
    char StrID[8] = {0}, Datetime[20] = {0}, StrEvent[8] = {0};

    dicop_callback_get_args(args, 3, "ID", StrID, "Datetime", Datetime, "Event", StrEvent);

    if (__SignalChangeCallback)
        __SignalChangeCallback(atoi(StrID), Datetime, atoi(StrEvent));
}

static void __AutoReportSignal(callback_args_t args)
{
    char StrID[8] = {0};
    char Datetime[20] = {0};
    char StrVolt[8] = {0};
    char StrCurr[8] = {0};
    char StrInstPower[8] = {0};
    char StrAccumPower[16] = {0};
    PCDReportData_t *Report;

    dicop_callback_get_args(args, 6, "ID", StrID, "Datetime", Datetime,
                            "Voltage", StrVolt, "Current", StrCurr,
                            "InstantPower", StrInstPower, "AccumPower", StrAccumPower);

    if (__AutoReportCallback) {
        Report = malloc(sizeof(PCDReportData_t));
        if (Report == NULL)
            return;

        Report->Voltage = atoi(StrVolt);
        Report->Current = atoi(StrCurr);
        Report->InstantPower = atoi(StrInstPower);
        Report->AccumPower = atoi(StrAccumPower);
        strncpy(Report->Datetime, Datetime, 20);

        __AutoReportCallback(atoi(StrID), Report);
    }
}


#ifdef TESTAPI

void AutoReportHandler(int ID, PCDReportData_t *Report)
{
    PRINT_INFO("AUTO REPORT: [%s] ID=%d, Volt=%d Curr=%d InstantPower=%d AccumPower=%d\n",
               Report->Datetime, ID,
               Report->Voltage, Report->Current,
               Report->InstantPower, Report->AccumPower);
}

void StateChangeHandler(int ID, char *Datetime, int Event)
{
    PRINT_INFO("STATE CHANGE: [%s] State=%d\n", Datetime, Event);
}

int main(int argc, char *argv[])
{
    int ConnID;
    PCDInfo_t *PCDInfo;
    PCDReportData_t *Report;
    char *MAC;
    int ID;
    int Voltage, Current, InstantPower, AccumPower;

    if (argc < 2)
        return 0;

    IPMS_InitAPI();

    ConnID = IPMS_Connect("192.168.1.230", 12250);
    if (ConnID <= 0) {
        fprintf(stderr, "Failed to connect to IPMS server\n");
        return -1;
    }

    PRINT_INFO("Start Register PCD Testing\n");
    PCDInfo = IPMS_PCDRegister(argv[1], "Chardi");
    if (PCDInfo)
        PRINT_INFO("PCD ID: %d\n\n", PCDInfo->ID);
    else
        PRINT_INFO("Unknown PCD [%s]\n\n", argv[1]);

    IPMS_InstallEventHandler(API_EVENT_STAT_CHANGE, StateChangeHandler);
    IPMS_InstallEventHandler(API_EVENT_AUTO_REPORT, AutoReportHandler);

    PRINT_INFO("Start Get MAC Testing\n");
    getchar();
    MAC = IPMS_PCDGetMAC(PCDInfo->ID);
    PRINT_INFO("MAC=%s\n\n", MAC);

    PRINT_INFO("Start Get ID Testing\n");
    getchar();
    ID = IPMS_PCDGetID(PCDInfo->MAC);
    PRINT_INFO("ID=%d\n\n", ID);

    PRINT_INFO("Start Get Report Testing\n");
    getchar();
    Report = IPMS_PCDGetReport(PCDInfo->ID);
    if (Report)
        PRINT_INFO("[%s] ID=%d, Volt=%d Curr=%d InstantPower=%d AccumPower=%d\n\n",
                   Report->Datetime, PCDInfo->ID,
                   Report->Voltage, Report->Current,
                   Report->InstantPower, Report->AccumPower);

    PRINT_INFO("Start Get Voltage Testing\n");
    getchar();
    Voltage = IPMS_PCDGetVoltage(PCDInfo->ID);
    PRINT_INFO("Voltage=%d\n\n", Voltage);

    PRINT_INFO("Start Get Current Testing\n");
    getchar();
    Current = IPMS_PCDGetCurrent(PCDInfo->ID);
    PRINT_INFO("Current=%d\n\n", Current);

    PRINT_INFO("Start Get Instant Power Testing\n");
    getchar();
    InstantPower = IPMS_PCDGetInstantPower(PCDInfo->ID);
    PRINT_INFO("InstantPower=%d\n\n", InstantPower);

    PRINT_INFO("Start Get Accumulated Power Testing\n");
    getchar();
    AccumPower = IPMS_PCDGetAccumPower(PCDInfo->ID);
    PRINT_INFO("AccumPower=%d\n\n", AccumPower);

    PRINT_INFO("Start PCD Control Testing: Turn On\n");
    getchar();
    IPMS_PCDControl(PCDInfo->ID, API_CTRL_TYPE_ONOFF, CTRL_ON, 1);

    PRINT_INFO("Start PCD Control Testing: Turn Off\n");
    getchar();
    IPMS_PCDControl(PCDInfo->ID, API_CTRL_TYPE_ONOFF, CTRL_OFF, 1);

    PRINT_INFO("Start Auto Report Testing\n");
    getchar();
    IPMS_AutoReportStart(PCDInfo->ID, 3, API_REPORT_SEL_ALL);

    PRINT_INFO("Stop Auto Report Testing\n");
    getchar();
    IPMS_AutoReportStop(PCDInfo->ID);

    PRINT_INFO("Testing done!\n");
    getchar();
    IPMS_Disconnect(ConnID);

    return 0;
}

#endif
